
#ifdef NO_FILE_MAP
  #include <stdio.h>
  #include <errno.h>
#elif defined(_WIN32)
  #include <windows.h>
#else
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <errno.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "input.h"
#define LOG_TAG "Input"
#include "log.h"

SourceInput Input;

// 载入Stream源文件至内存
// 可选择标准库直接读取/文件内存映射
void ReadSourceFile(const char *filename)
{
    Input.fileName = filename;

#ifdef NO_FILE_MAP
    // 使用C语言标准库函数加载代码
    Input.file = fopen(filename, "rb");
    if (Input.file == NULL) {
        LOGE("%s: %s", strerror(errno), filename);
        exit(EXT_FileOpenFail);
    }

    // 获得文件的大小
    fseek(Input.file, 0, SEEK_END);
    Input.size = ftell(Input.file);
    // 申请源码的缓存
    Input.base = malloc(Input.size);
    if (Input.base == NULL) {
        LOGE("Memory not enough");
        fclose(Input.file);
        exit(EXT_MemoryFull);
    }

    // 读取文件全部内容
    fseek(Input.file, 0, SEEK_SET);
    Input.size = fread(Input.base, 1, Input.size, Input.file);
    fclose(Input.file);

#elif defined(_WIN32)
    // 使用Win32 API,通过内存映射加载代码
    Input.file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (Input.file == INVALID_HANDLE_VALUE) {
        LOGE("%s: %s", strerror(GetLastError()), filename);
        exit(EXT_FileOpenFail);
    }

    Input.size = GetFileSize(Input.file, NULL);
    HANDLE fm = CreateFileMapping(Input.file, NULL, PAGE_READONLY, 0, Input.size, NULL);
    if (fm == NULL) {
        LOGE("%s: %s", strerror(GetLastError()), filename);
        CloseHandle(Input.file);
        exit(EXT_FileOpenFail);
    }

    Input.base = (char *)MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0);
    CloseHandle(fm);
    if (Input.base == NULL) {
        LOGE("%s: %s", strerror(GetLastError()), filename);
        CloseHandle(Input.file);
        exit(EXT_FileOpenFail);
    }

#else
    // 使用类Linux库函数,通过内存映射加载代码
    int fd = open(filename, O_RDONLY);
    if (fd == -1) {
        LOGE("%s: %s", strerror(errno), filename);
        exit(EXT_FileOpenFail);
    }
    Input.file = (void *) fd;

    // 获取文件大小
    struct stat st;
    if (fstat(fd, &st) == -1) {
        LOGE("%s: %s", strerror(errno), filename);
        close(fd);
        exit(EXT_FileOpenFail);
    }
    Input.size = st.st_size;

    Input.base = mmap(NULL, Input.size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (Input.base == MAP_FAILED) {
        LOGE("%s: %s", strerror(errno), filename);
        close(fd);
        exit(EXT_FileOpenFail);
    }

#endif

    Input.cursor = Input.lineHead = Input.base;
    Input.line = 1;
}

void ReadMemory(const char *src, int len)
{
    Input.fileName = "";
    Input.file = NULL;
    Input.base = (char*) src;
    Input.size = (len < 0) ? strlen(src) : len;
    Input.cursor = Input.lineHead = Input.base;
    Input.line = 1;
}

void CloseSourceFile(void)
{
#ifdef NO_FILE_MAP
    free(Input.base);

#elif defined(_WIN32)
    UnmapViewOfFile(Input.base);
    CloseHandle(Input.file);

#else
    close((int) Input.file);
    munmap(Input.base, Input.size);

#endif

    memset(&Input, 0, sizeof(Input));
}
